#ifndef MW_COMMON_H
#define MW_COMMON_H


/*
  Meanwhile - Unofficial Lotus Sametime client library
  
  Copyright (c) 2007 Christopher O'Brien <siege@preoccupied.net>
  
  License: LGPL
  
  This library is free software; you can redistribute it and/or modify
  it under the terms of version 2 the GNU Library General Public
  License as published by the Free Software Foundation.
                                                                                
  This library is distributed in the hope that it will be useful, but
  WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Library General Public License for more details.
  
  You should have received a copy of the GNU Library General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
  USA
*/


/** @file mw-common.h
    @since 2.0.0

    Utility functions for reading and writing the basic types used in
    binary messages, for logging, and for errors */ 


#include <stdarg.h>
#include <glib.h>


G_BEGIN_DECLS


typedef (gpointer)(*MwNewFunc)();

typedef (gpointer)(*MwDupFunc)(const gpointer item);

typedef (void)(*MwCopyFunc)(gpointer to, const gpointer from);

typedef (void)(*MwReadFunc)(MwReadBuffer *readb, gpointer item,
			    GError **err);

typedef (void)(*MwWriteFunc)(MwWriteBuffer *writeb, const gpointer item);

typedef (void)(*MwClearFunc)(gpointer item);

typedef (void)(*MwFreeFunc)(gpointer item);

typedef (guint)(*MwHashFunc)(const gpointer item);

typedef (gboolean)(*MwEqualFunc)(const gpointer a, const gpointer b);



typedef struct mw_read_buffer MwReadBuffer;


/** allocate a MwReadBuffer with a copy of the given data */
MwReadBuffer *mw_read_buffer_new_copy(const gpointer data, gsize len);


/** allocate a MwReadBuffer with the given data */
MwReadBuffer *mw_read_buffer_new_wrap(const gpoinder data, gsize len);


/** deallocate a MwReadBuffer. If the buffer was created with
    mw_read_buffer_new_copy, then the copied data will also be
    free'd. If the buffer was created with mw_read_buffer_new_wrap,
    the data will not be free'd */
void mw_read_buffer_free(MwReadBuffer *mwrb);


gsize mw_read_buffer_get_remaining(MwReadBuffer *mwrb);


/** attempt to copy len bytes from the buffer onto mem. If len is
    greater than the amount of data left undread in the buffer, only
    the remaining bytes will be copied. This function returns the
    count of bytes copied */
gsize mw_read_buffer_read(MwReadBuffer *mwrb, gpointer mem, gsize len);


/** attempt to skip len bytes of the buffer. If len is greater than
    the amount of data left unread in the buffer, it will skip to the
    end. This function returns the count of bytes skipped. */
gsize mw_read_buffer_advance(MwReadBuffer *mwrb, gsize len);


void mw_read_buffer_reset(MwReadBuffer *mwrb);


/** attempt to unpack a sequence of values from a read buffer. The
    format of the unpacker definition uses a single character to
    indicate one of the basic types: b, gboolean*; u, guint16*; U,
    guint32*; s, gchar**; o, MwOpaque*; z, extended

    Each character should have a single value in the value list, of
    the type indicated above, with the exception of z. The extended
    type takes two variables: a pointer to the value to be passed to
    an unpacker function, and a pointer to the unpacker function
    itself. Unpacker functions must be of the type mw_read_func. Any
    errors in unpacking will be propagated to err, and will terminate
    unpacking. */
void mw_read_buffer_unpack(MwReadBuffer *mwrb, GError **err,
			   const gchar *defn, ...);


#define MW_UNPACK(mwrb, err, defn...)		\
  mw_read_buffer_unpack((mwrb), (err), defn);


void mw_read_buffer_unpackv(MwReadBuffer *mwrb, GError **err,
			    const gchar *defn, va_list vargs);


void mw_read_buffer_read_items(MwReadBuffer *mwrb, GError **err,
			       gsize item_size,
			       guint count, gpointer *items,
			       mw_read_func unpack);


#define MW_READ_ITEMS(mwrb, err, type, count, items, unpack)		\
  mw_read_buffer_read_items((mwrb), (err), sizeof((type)),		\
			    (count), (items), (unpack))


typedef struct mw_write_buffer MwWriteBuffer;


/** the same as calling mw_write_buffer_new_sized(0) */
MwWriteBuffer *mw_write_buffer_new();


/** allocate a new write buffer with at least len bytes already
    available */
MwWriteBuffer *mw_write_buffer_new_sized(gsize len);


/** de-allocate the buffer structure, and places references to its
    underlying data into *data and the length into *len */
void mw_write_buffer_finalize(MwWriteBuffer *mwwb,
			      gpointer *data, gsize *len);


/** de-allocate the buffer structure and its underlying data */
void mw_write_buffer_free(MwWriteBuffer *mwwb);


/** write data onto the buffer, growing its internal storage as
    necessary */
void mw_write_buffer_write(MwWriteBuffer *mwwb, gpointer data, gsize len);


/** packs a sequence of values into a write buffer.

    defn uses the same format as mw_read_buffer_unpack, with the
    exception that the extended type requires a packer function, which
    must be of the type mw_write_func */
void mw_write_buffer_pack(MwWriteBuffer *mwwb,
			  const gchar *defn, ...);


#define MW_PACK(mwwb, defn...)			\
  mw_read_buffer_unpack((mwwb), defn);


void mw_write_buffer_packv(MwWriteBuffer *mwwb,
			   const gchar *defn, va_list vargs);


void mw_write_buffer_write_items(MwWriteBuffer *mwwb,
				 gsize item_size,
				 guint count, gpointer items,
				 mw_write_func pack);


#define MW_WRITE_ITEMS(mwwb, type, count, items, pack)			\
  mw_write_buffer_write_items((mwwb), sizeof((type)),			\
			      (count), (items), (pack))



void mw_copy_items(gpointer *to,
		   guint count, gsize item_size, const gpointer items,
		   mw_copy_func copyf);


gpointer mw_dup_items(guint count, gsize item_size, const gpointer items,
		      mw_copy_func copyf);


#define MW_DUP_ITEMS(count, type, items, copy_func)		\
  mw_dup_items((count), sizeof((type)), (items), (copy_func))



void mw_clear_items(guint count, gsize item_size, gpointer items,
		    mw_clear_func clearf);


#define MW_CLEAR_ITEMS(count, type, items, clear_func)			\
  mw_clear_items((count), sizeof((type)), (items), (clear_func))



void mw_free_items(guint count, gsize item_size, gpointer items,
		   mw_clear_func clearf);


#define MW_FREE_ITEMS(count, type, items, clear_func)		\
  mw_free_items((count), sizeof((type)), (items), (clear_func))



/** @section Simple types
    @{ */


void mw_uint16_read(MwReadBuffer *mwb, guint16 *val, GError **err);


guint16 mw_uint16_peek(MwReadBuffer *mwb, GError **err);


void mw_uint16_write(MwWriteBuffer *mwb, guint16 val, GError **err);



void mw_uint32_read(MwReadBuffer *mwb, guint32 *val, GError **err);


guint32 mw_uint32_peek(MwReadBuffer *mwb, GError **err);


void mw_uint32_write(MwWriteBuffer *mwb, guint32 val, GError **err);



void mw_boolean_read(MwReadBuffer *mwb, gboolean *val, GError **err);


gboolean mw_boolean_peek(MwReadBuffer *mwb, GError **err);


void mw_boolean_write(MwWriteBuffer *mwb, gboolean val, GError **err);



void mw_str_read(MwReadBuffer *mwb, gchar **str, GError **err);


void mw_str_write(MwWriteBuffer *mwb, const gchar *str, GError **err);



typedef struct mw_opaque {
  gsize len;
  guchar *data;
} MwOpaque;


GType mw_opaque_get_type();


#define MW_TYPE_OPAQUE (mw_opaque_get_type())


MwOpaque *mw_opaque_new();


MwOpaque *mw_opaque_dup(const MwOpaque *mwo);


void mw_opaque_copy(MwOpaque *to, const MwOpaque *from);


void mw_opaque_read(MwReadBuffer *mwb, MwOpaque *mwo, GError **err);


void mw_opaque_write(MwWriteBuffer *mwb, const MwOpaque *mwo);


void mw_opaque_clear(MwOpaque *mwo);


void mw_opaque_free(MwOpaque *mwo);


guint mw_opaque_hash(const MwOpaque *mwo);


gboolean mw_opaque_equal(const MwOpaque *a, const MwOpaque *b);


/** @} */



/** @section Compound types
    @{ */


typedef struct mw_identity {
  gchar *user;
  gchar *community;
} MwIdentity;


GType mw_identity_get_type();


#define MW_TYPE_IDENTITY (mw_identity_get_type())


MwIdentity *mw_identity_new();


MwIdentity *mw_identity_dup(const MwIdentity *id);


void mw_identity_copy(MwIdentity *to, const MwIdentity *from);


void mw_identity_read(MwReadBuffer *mwb, MwIdentity *id, GError **err);


void mw_identity_write(MwWriteBuffer *mwb, const MwIdentity *id);


void mw_identity_clear(MwIdentity *id);


void mw_identity_free(MwIdentity *id);


guint mw_identity_hash(const MwIdentity *id);


gboolean mw_identity_equal(const MwIdentity *a, const MwIdentity *b);


/** This is not a definitive list. Applications wishing to display
    client type information will need to compile their own list of
    type values, though a semi-complete list is available on the
    Meanwhile project wiki */
typedef enum mw_client_type {
  MW_CLIENT_TYPE_LIB = 0x1000,
  MW_CLIENT_TYPE_JAVA_WEB = 0x1001,
  MW_CLIENT_TYPE_BINARY = 0x1002,
  MW_CLIENT_TYPE_JAVA_APP = 0x1003,
  MW_CLIENT_TYPE_ST_LINKS = 0x100a,

  /** 0xff mask for Notes clients */
  MW_CLIENT_TYPE_MASK_NOTES = 0x1200,

  /** 0xff mask for ICT clients */
  MW_CLIENT_TYPE_MASK_ICT = 0x1300,

  /** 0xff mask for NotesBuddy clients */
  MW_CLIENT_TYPE_MASK_NOTESBUDDY = 0x1400,

  MW_CLIENT_TYPE_MEANWHILE = 0x1700,

} MwClientType;


GType mw_client_type_get_type();


#define MW_TYPE_CLIENT_TYPE (mw_client_type_get_type())



typedef struct mw_login {
  MwIdentity id;
  gchar *name;
  gchar *login_id;
  guint16 client_type;

  gboolean full;
  gchar *desc;
  guint32 ip_addr;
  gchar *server_id;

} MwLogin;


GType mw_login_get_type();


#define MW_TYPE_LOGIN (mw_login_get_type())


MwLogin *mw_login_new();


MwLogin *mw_login_dup(const MwLogin *login);


void mw_login_copy(MwLogin *to, const MwLogin *from);


void mw_login_read(MwReadBuffer *mwb, MwLogin *login, GError *err);


void mw_login_write(MwWriteBuffer *mwb, const MwLogin *login);


void mw_login_clear(MwLogin *login);


void mw_login_free(MwLogin *login);


guint mw_login_hash(const MwLogin *login);


gboolean mw_login_equal(const MwLogin *a, const MwLogin *b);



typedef enum mw_status_mode {
  MW_STATUS_MODE_OFFLINE = 0x0000,
  MW_STATUS_MODE_ACTIVE = 0x0020,
  MW_STATUS_MODE_IDLE = 0x0040,
  MW_STATUS_MODE_AWAY = 0x0060,
  MW_STATUS_MODE_BUSY = 0x0080,

  MW_STATUS_MODE_MASK_MOBILE = 0x0200,
} MwStatusMode;


GType mw_status_mode_get_type();


#define MW_TYPE_STATUS_MODE (mw_status_mode_get_type())



typedef struct mw_status {
  guint16 mode;
  guint32 time;
  gchar *desc;
  
} MwStatus;


GType mw_status_get_type();


#define MW_TYPE_STATUS (mw_status_get_type())


MwStatus *mw_status_new();


MwStatus *mw_status_dup(const MwStatus *status);


void mw_status_copy(MwStatus *to, const MwStatus *from);


void mw_status_read(MwReadBuffer *mwrb, MwStatus *status, GError **err);


void mw_status_write(MwWriteBuffer *mwwb, MwStatus *status);


void mw_status_clear(MwStatus *status);


void mw_status_free(MwStatus *status);


guint mw_status_hash(const MwStatus *status);


gboolean mw_status_equal(const MwStatus *a, const MwStatus *b);



typedef struct mw_privacy {
  gboolean deny;
  guint32 count;
  MwIdentity *users;

} MwPrivacy;


GType mw_privacy_get_type();


#define MW_TYPE_PRIVACY (mw_privacy_get_type())


MwPrivacy *mw_privacy_new();


MwPrivacy *mw_privacy_dup(const MwPrivacy *privacy);


void mw_privacy_copy(MwPrivacy *to, const MwPrivacy *from);


void mw_privacy_read(MwReadBuffer *mwrb, MwPrivacy *privacy, GError **err);


void mw_privacy_write(MwWriteBuffer *mwwb, const MwPrivacy *privacy);


void mw_privacy_clear(MwPrivacy *privacy);


void mw_privacy_free(MwPrivacy *privacy);


guint mw_privacy_hash(const MwPrivacy *privacy);


gboolean mw_privacy_equal(const MwPrivacy *privacy);


/** @} */



/** @section Logging and debugging
    @{ */


/** wrap gchar* in this when using logging functions. On some sensible
    opertating systems, a NULL string to a printf function will be
    represented as "(null)", but on others, it will result in a
    segfault. This macro will replace NULL strings with "(null)". Be
    aware that str will be evaluated twice if it is non-NULL. */
#define NSTR(str) ((str)? (str): "(null)")


#ifndef MW_LOG_DOMAIN
#define MW_LOG_DOMAIN "Meanwhile2"
#endif


#define mw_log_set_handler(level, func, user_data)			\
  g_log_set_handler((MW_LOG_DOMAIN), (level), (func), (user_data))


#define mw_log_remove_handler(handler_id)	\
  g_log_remove_handler((MW_LOG_DOMAIN), (handler_id))


#define mw_log(level, format...)		\
  g_log((MW_LOG_DOMAIN), (level), format)


#define mw_logv(level, format, vargs)			\
  g_logv((MW_LOG_DOMAIN), (level), (format), (vargs))


#define mw_error(format...)			\
  mw_log(G_LOG_LEVEL_ERROR, format)


#define mw_errorv(format, vargs)		\
  mw_logv(G_LOG_LEVEL_ERROR, (format), (vargs))


#define mw_critical(format...)			\
  mw_log(G_LOG_LEVEL_CRITICAL, format)


#define mw_criticalv(format, vargs)			\
  mw_logv(G_LOG_LEVEL_CRITICAL, (format), (vargs))


#define mw_warning(format...)			\
  mw_log(G_LOG_LEVEL_WARNING, format)


#define mw_warningv(format, vargs)			\
  mw_logv(G_LOG_LEVEL_WARNING, (format), (vargs))


#define mw_message(format...)			\
  mw_log(G_LOG_LEVEL_MESSAGE, format)


#define mw_messagev(format, vargs)			\
  mw_logv(G_LOG_LEVEL_MESSAGE, (format), (vargs))


#define mw_info(format...)			\
  mw_log(G_LOG_LEVEL_INFO, format)


#define mw_infov(format, vargs)			\
  mw_logv(G_LOG_LEVEL_INFO, (format), (vargs))


#define mw_debug(format...)			\
  mw_log(G_LOG_LEVEL_DEBUG, format)


#define mw_debugv(format, vargs)		\
  mw_logv(G_LOG_LEVEL_DEBUG, (format), (vargs))



#ifndef MW_ERROR

/** mask for all Meanwhile2 errors (as opposed to pre-defined Sametime
    protocol errors, which would be presented in the same GError
    domain) */
#define MW_ERROR 0xf0000000
#endif


typedef enum mw_error_code_read {
  /** mask for all Meanwhile read errors */
  MW_ERROR_READ = MW_ERROR | 0x1000,

  /** an error reading a uint16 */
  MW_ERROR_READ_UINT16 = MW_ERROR_READ | 0x01,

  /** an error reading a uint32 */
  MW_ERROR_READ_UINT32 = MW_ERROR_READ | 0x02,

  /** an error reading a boolan */
  MW_ERROR_READ_BOOLEAN = MW_ERROR_READ | 0x03,

  /** an error reading a string */
  MW_ERROR_READ_STR = MW_ERROR_READ | 0x04,

  /** an error reading a MwOpaque */
  MW_ERROR_READ_OPAQUE = MW_ERROR_READ | 0x05,

  /** an error in an unpack */
  MW_ERROR_READ_UNPACK = MW_ERROR_READ | 0x10,

} MwErrorCodeRead;



GQuark mw_error_quark();


#define mw_error_new(code, format...)		\
  g_error_new(mw_error_quark(), (code), format)


#define mw_error_new_literal(code, message)			\
  g_error_new_literal(mw_error_quark(), (code), (message))


#define mw_set_error(err, code, format...)		\
  g_set_error(err, mw_error_quark(), (code), format)


/** modify an existing error to prepend the message with
    file:function:line information. The error will retain its original
    domain and code */
void mw_line_error(GError *err,
		   const gchar *file, const gchar *func, gint line);


/** prepend a message and newline to an existing error */
void mw_wrap_error(GError *err, const gchar *frmt, ...);


/** prepend a message and newline to an existing error */
void mw_wrap_errorv(GError *err, const gchar *frmt, va_list vargs);


/** macro for mw_line_error that fills in file, func, and line with
    their current values */
#define MW_LINE_ERROR(err)					\
  mw_line_error((err), __FILE__, __FUNCTION__, __LINE__)


/** if GError **error is set to a non-empty error, then stmt is
    executed. After stmt is executed, error is checked again. If it is
    non-empty, then file:func:line information is wrapped into it via
    MW_LINE_ERROR. Subsequent calls to MW_TRYLINE pointing to the same
    error will not be executed until the error is cleared. */
#define MW_TRYLINE(error, stmt)						\
  {									\
    GError **_mw_watch = (error);					\
    if(! *_mw_watch) {							\
      stmt;								\
      if(*_mw_watch) {							\
	MW_LINE_ERROR(*_mw_watch);					\
      }									\
    }									\
  }									\


G_END_DECLS


#endif /* MW_COMMON_H */
